﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Isuka.Iteration
{
    /// <summary>
    /// リストに対する反復子
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public sealed class ListIterator<T>
        : IRandomAccessIterator<T>
        , IReWritableIterator<T>
    {
        readonly IList<T> container;
        int index;

        public ListIterator(IList<T> container)
            : this(container, 0)
        {
            Contract.Requires(container != null);
        }

        /// <summary>
        /// コンストラクター
        /// </summary>
        /// <param name="container"></param>
        /// <param name="index"></param>
        public ListIterator(IList<T> container, int index)
        {
            Contract.Requires<ArgumentNullException>(container != null, "container");
            Contract.Requires<IndexOutOfRangeException>(0 <= index && index <= container.Count);

            this.container = container;
            this.index = index;
        }

        bool EqualsCore(ListIterator<T> other)
        {
            Contract.Requires(other != null);
            return container == other.container && index == other.index;
        }

        public override string ToString()
        {
            return string.Format("({0}, {1})", container, index);
        }

        #region IRandomAccessIterator メンバー

        public int Index
        {
            get { return index; }
        }

        #endregion

        #region IIterator<T> メンバー

        public T Current
        {
            get
            {
                Gate.InRange(index, container);
                return container[index];
            }
            set
            {
                Gate.InRange(index, container);
                container[index] = value;
            }
        }

        #endregion

        #region IBidirectionalIterator メンバー

        public void MovePrev()
        {
            checked
            {
                index--;
            }
        }

        #endregion

        #region IIterator メンバー

        public bool Advance(int diff)
        {
            checked
            {
                index += diff;
                return InRange;
            }
        }

        public int? Bias
        {
            get { return index; }
        }

        public object Container
        {
            get { return container; }
        }

        object IIterator.Current
        {
            get { return Current; }
        }

        public int? Distance(IIterator other)
        {
            var it = other as ListIterator<T>;
            if (it != null && container == it.container)
            {
                return it.index - index;
            }
            return null;
        }

        public bool Equals(IIterator other)
        {
            if (other == null)
            {
                return !InRange;
            }
            else
            {
                if (other is ListIterator<T>)
                {
                    return EqualsCore((ListIterator<T>)other);
                }
            }
            return false;
        }

        public bool InRange
        {
            get
            {
                return 0 <= index && index < container.Count;
            }
        }

        public void MoveNext()
        {
            checked
            {
                index++;
            }
        }

        #endregion

        #region ICloneable メンバー

        public object Clone()
        {
            return MemberwiseClone();
        }

        #endregion

        #region Object メンバー

        // override object.Equals
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return !InRange;
            }
            else
            {
                if (obj is ListIterator<T>)
                {
                    return EqualsCore((ListIterator<T>)obj);
                }
            }
            return false;
        }

        // override object.GetHashCode
        public override int GetHashCode()
        {
            // TODO: write your implementation of GetHashCode() here
            return container.GetHashCode();
        }

        #endregion
    }
}
